/*
 * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.engine.test.api.tenant;

import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

import java.util.ArrayList;
import java.util.List;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.test.PluggableActivitiTestCase;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Job;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * A test case for the various implications of the tenancy support (tenant id column to entities + query support)
 */
public class TenancyTest extends PluggableActivitiTestCase {

    private static final String TEST_TENANT_ID = "myTenantId";

    private List<String> autoCleanedUpDeploymentIds = new ArrayList<String>();

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        this.autoCleanedUpDeploymentIds.clear();
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();

        if (!autoCleanedUpDeploymentIds.isEmpty()) {
            for (String deploymentId : autoCleanedUpDeploymentIds) {
                repositoryService.deleteDeployment(deploymentId, true);
            }
        }
    }

    /**
     * Deploys the one task process with the test tenant id.
     * @return The process definition id of the deployed process definition.
     */
    private String deployTestProcessWithTestTenant() {
        return deployTestProcessWithTestTenant(TEST_TENANT_ID);
    }

    private String deployTestProcessWithTestTenant(String tenantId) {
        String id = repositoryService
            .createDeployment()
            .addBpmnModel("testProcess.bpmn20.xml", createOneTaskTestProcess())
            .tenantId(tenantId)
            .deploy()
            .getId();

        autoCleanedUpDeploymentIds.add(id);

        return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
    }

    private String deployTestProcessWithTwoTasksWithTestTenant() {
        String id = repositoryService
            .createDeployment()
            .addBpmnModel("testProcess.bpmn20.xml", createTwoTasksTestProcess())
            .tenantId(TEST_TENANT_ID)
            .deploy()
            .getId();

        autoCleanedUpDeploymentIds.add(id);

        return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
    }

    private String deployTestProcessWithTwoTasksNoTenant() {
        String id = repositoryService
            .createDeployment()
            .addBpmnModel("testProcess.bpmn20.xml", createTwoTasksTestProcess())
            .deploy()
            .getId();

        autoCleanedUpDeploymentIds.add(id);

        return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
    }

    public void testDeploymentTenancy() {
        deployTestProcessWithTestTenant();

        assertThat(repositoryService.createDeploymentQuery().singleResult().getTenantId()).isEqualTo(TEST_TENANT_ID);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(TEST_TENANT_ID).list()).hasSize(1);
        assertThat(
            repositoryService
                .createDeploymentQuery()
                .deploymentId(autoCleanedUpDeploymentIds.get(0))
                .deploymentTenantId(TEST_TENANT_ID)
                .list()
        ).hasSize(1);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("my%").list()).hasSize(1);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("%TenantId").list()).hasSize(1);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("m%Ten%").list()).hasSize(1);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("noexisting%").list()).hasSize(0);
        assertThat(repositoryService.createDeploymentQuery().deploymentWithoutTenantId().list()).hasSize(0);
    }

    public void testProcessDefinitionTenancy() {
        // Deploy a process with tenant and verify

        String processDefinitionIdWithTenant = deployTestProcessWithTestTenant();
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionIdWithTenant)
                .singleResult()
                .getTenantId()
        ).isEqualTo(TEST_TENANT_ID);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
        ).hasSize(1);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
            1
        );
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
        ).hasSize(0);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
            0
        );

        // Deploy another process, without tenant
        String processDefinitionIdWithoutTenant = deployOneTaskTestProcess();
        assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(2);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
        ).hasSize(1);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
            1
        );
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
        ).hasSize(0);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
            1
        );

        // Deploy another process with the same tenant
        String processDefinitionIdWithTenant2 = deployTestProcessWithTestTenant();
        assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(3);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
        ).hasSize(2);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
            2
        );
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
        ).hasSize(0);
        assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
            1
        );

        // Extra check: we deployed the one task process twice, but once with
        // tenant and once without. The latest query should show this.
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(TEST_TENANT_ID)
                .latestVersion()
                .singleResult()
                .getId()
        ).isEqualTo(processDefinitionIdWithTenant2);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId("Not a tenant")
                .latestVersion()
                .count()
        ).isEqualTo(0);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionWithoutTenantId()
                .latestVersion()
                .singleResult()
                .getId()
        ).isEqualTo(processDefinitionIdWithoutTenant);
    }

    public void testProcessInstanceTenancy() {
        // Start a number of process instances with tenant
        String processDefinitionId = deployTestProcessWithTestTenant();
        int nrOfProcessInstancesWithTenant = 6;
        for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionId);
        }

        // Start a number of process instance without tenantit
        String processDefinitionIdNoTenant = deployOneTaskTestProcess();
        int nrOfProcessInstancesNoTenant = 8;
        for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
        }

        // Check the query results
        assertThat(
            runtimeService
                .createProcessInstanceQuery()
                .processDefinitionId(processDefinitionId)
                .list()
                .get(0)
                .getTenantId()
        ).isEqualTo(TEST_TENANT_ID);
        assertThat(runtimeService.createProcessInstanceQuery().list().size()).isEqualTo(
            nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
        );
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list().size()
        ).isEqualTo(nrOfProcessInstancesNoTenant);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list().size()
        ).isEqualTo(nrOfProcessInstancesWithTenant);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantIdLike("%enan%").list().size()
        ).isEqualTo(nrOfProcessInstancesWithTenant);
    }

    public void testExecutionTenancy() {
        // Start a number of process instances with tenant
        String processDefinitionId = deployTestProcessWithTwoTasksWithTestTenant();
        int nrOfProcessInstancesWithTenant = 4;
        for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionId);
        }

        // Start a number of process instance without tenantid
        String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
        int nrOfProcessInstancesNoTenant = 2;
        for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
        }

        // Check the query results:
        // note: 3 executions per process instance due to parallelism!
        assertThat(
            runtimeService.createExecutionQuery().processDefinitionId(processDefinitionId).list().get(0).getTenantId()
        ).isEqualTo(TEST_TENANT_ID);
        assertThat(
            runtimeService
                .createExecutionQuery()
                .processDefinitionId(processDefinitionIdNoTenant)
                .list()
                .get(0)
                .getTenantId()
        ).isEqualTo("");
        assertThat(runtimeService.createExecutionQuery().list().size()).isEqualTo(
            3 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
        );
        assertThat(runtimeService.createExecutionQuery().executionWithoutTenantId().list().size()).isEqualTo(
            3 * nrOfProcessInstancesNoTenant
        );
        assertThat(runtimeService.createExecutionQuery().executionTenantId(TEST_TENANT_ID).list().size()).isEqualTo(
            3 * nrOfProcessInstancesWithTenant
        );
        assertThat(runtimeService.createExecutionQuery().executionTenantIdLike("%en%").list().size()).isEqualTo(
            3 * nrOfProcessInstancesWithTenant
        );

        // Check the process instance query results, just to be sure
        assertThat(
            runtimeService
                .createProcessInstanceQuery()
                .processDefinitionId(processDefinitionId)
                .list()
                .get(0)
                .getTenantId()
        ).isEqualTo(TEST_TENANT_ID);
        assertThat(runtimeService.createProcessInstanceQuery().list().size()).isEqualTo(
            nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
        );
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list().size()
        ).isEqualTo(nrOfProcessInstancesNoTenant);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list().size()
        ).isEqualTo(nrOfProcessInstancesWithTenant);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantIdLike("%en%").list().size()
        ).isEqualTo(nrOfProcessInstancesWithTenant);
    }

    public void testTaskTenancy() {
        // Generate 10 tasks with tenant
        String processDefinitionIdWithTenant = deployTestProcessWithTwoTasksWithTestTenant();
        int nrOfProcessInstancesWithTenant = 5;
        for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
        }

        // Generate 4 tasks without tenant
        String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
        int nrOfProcessInstancesNoTenant = 2;
        for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
        }

        // Check the query results
        assertThat(
            taskService.createTaskQuery().processDefinitionId(processDefinitionIdWithTenant).list().get(0).getTenantId()
        ).isEqualTo(TEST_TENANT_ID);
        assertThat(
            taskService.createTaskQuery().processDefinitionId(processDefinitionIdNoTenant).list().get(0).getTenantId()
        ).isEqualTo("");

        assertThat(taskService.createTaskQuery().list().size()).isEqualTo(14);
        assertThat(taskService.createTaskQuery().taskTenantId(TEST_TENANT_ID).list().size()).isEqualTo(10);
        assertThat(taskService.createTaskQuery().taskTenantId("Another").list().size()).isEqualTo(0);
        assertThat(taskService.createTaskQuery().taskTenantIdLike("my%").list().size()).isEqualTo(10);
        assertThat(taskService.createTaskQuery().taskWithoutTenantId().list().size()).isEqualTo(4);
    }

    public void testJobTenancy() {
        // Deploy process with a timer and an async step AND with a tenant
        String deploymentId = repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy()
            .getId();

        // verify job (timer start)
        Job job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());

        // Verify Job tenancy (process intermediary timer)
        job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);

        // Start process, and verify async job has correct tenant id
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());
        job = managementService.createJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);

        // Finish process
        managementService.executeJob(job.getId());

        // Do the same, but now without a tenant
        String deploymentId2 = repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
            .deploy()
            .getId();

        job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo("");
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());
        job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo("");
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());
        job = managementService.createJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo("");

        // clean up
        repositoryService.deleteDeployment(deploymentId, true);
        repositoryService.deleteDeployment(deploymentId2, true);
    }

    public void testModelTenancy() {
        // Create a few models with tenant
        int nrOfModelsWithTenant = 3;
        for (int i = 0; i < nrOfModelsWithTenant; i++) {
            Model model = repositoryService.newModel();
            model.setName(i + "");
            model.setTenantId(TEST_TENANT_ID);
            repositoryService.saveModel(model);
        }

        // Create a few models without tenant
        int nrOfModelsWithoutTenant = 5;
        for (int i = 0; i < nrOfModelsWithoutTenant; i++) {
            Model model = repositoryService.newModel();
            model.setName(i + "");
            repositoryService.saveModel(model);
        }

        // Check query
        assertThat(repositoryService.createModelQuery().list().size()).isEqualTo(
            nrOfModelsWithoutTenant + nrOfModelsWithTenant
        );
        assertThat(repositoryService.createModelQuery().modelWithoutTenantId().list().size()).isEqualTo(
            nrOfModelsWithoutTenant
        );
        assertThat(repositoryService.createModelQuery().modelTenantId(TEST_TENANT_ID).list().size()).isEqualTo(
            nrOfModelsWithTenant
        );
        assertThat(repositoryService.createModelQuery().modelTenantIdLike("my%").list().size()).isEqualTo(
            nrOfModelsWithTenant
        );
        assertThat(repositoryService.createModelQuery().modelTenantId("a%").list().size()).isEqualTo(0);

        // Clean up
        for (Model model : repositoryService.createModelQuery().list()) {
            repositoryService.deleteModel(model.getId());
        }
    }

    public void testChangeDeploymentTenantId() {
        // Generate 8 tasks with tenant
        String processDefinitionIdWithTenant = deployTestProcessWithTwoTasksWithTestTenant();
        int nrOfProcessInstancesWithTenant = 4;
        for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
        }

        // Generate 10 tasks without tenant
        String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
        int nrOfProcessInstancesNoTenant = 5;
        for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
            runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
        }

        // Migrate deployment with tenant to another tenant
        String newTenantId = "NEW TENANT ID";

        String deploymentId = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionId(processDefinitionIdWithTenant)
            .singleResult()
            .getDeploymentId();
        repositoryService.changeDeploymentTenantId(deploymentId, newTenantId);

        // Verify tenant id
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        assertThat(deployment.getTenantId()).isEqualTo(newTenantId);

        // Verify deployment
        assertThat(repositoryService.createDeploymentQuery().list()).hasSize(2);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(TEST_TENANT_ID).list()).hasSize(0);
        assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(newTenantId).list()).hasSize(1);
        assertThat(repositoryService.createDeploymentQuery().deploymentWithoutTenantId().list()).hasSize(1);

        // Verify process definition
        assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(2);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
        ).hasSize(0);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(newTenantId).list()
        ).hasSize(1);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(newTenantId).list()
        ).hasSize(1);

        // Verify process instances
        assertThat(runtimeService.createProcessInstanceQuery().list()).hasSize(
            nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
        );
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list()).hasSize(
            0
        );
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceTenantId(newTenantId).list()).hasSize(
            nrOfProcessInstancesWithTenant
        );
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list()).hasSize(
            nrOfProcessInstancesNoTenant
        );

        // Verify executions
        assertThat(runtimeService.createExecutionQuery().list()).hasSize(
            3 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
        );
        assertThat(runtimeService.createExecutionQuery().executionWithoutTenantId().list()).hasSize(
            3 * nrOfProcessInstancesNoTenant
        );
        assertThat(runtimeService.createExecutionQuery().executionTenantId(TEST_TENANT_ID).list()).hasSize(0);
        assertThat(runtimeService.createExecutionQuery().executionTenantId(newTenantId).list()).hasSize(
            3 * nrOfProcessInstancesWithTenant
        );
        assertThat(runtimeService.createExecutionQuery().executionTenantIdLike("NEW%").list()).hasSize(
            3 * nrOfProcessInstancesWithTenant
        );

        // Verify tasks
        assertThat(taskService.createTaskQuery().list()).hasSize(
            2 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
        );
        assertThat(taskService.createTaskQuery().taskTenantId(TEST_TENANT_ID).list()).hasSize(0);
        assertThat(taskService.createTaskQuery().taskTenantId(newTenantId).list()).hasSize(
            2 * nrOfProcessInstancesWithTenant
        );
        assertThat(taskService.createTaskQuery().taskWithoutTenantId().list()).hasSize(
            2 * nrOfProcessInstancesNoTenant
        );

        // Remove the tenant id and verify results
        // should clash: there is already a process definition with the same key
        assertThatExceptionOfType(Exception.class).isThrownBy(() ->
            repositoryService.changeDeploymentTenantId(deploymentId, "")
        );
    }

    public void testChangeDeploymentIdWithClash() {
        String processDefinitionIdWithTenant = deployTestProcessWithTestTenant("tenantA");
        deployOneTaskTestProcess();

        // Changing the one with tenant now back to one without should clash,
        // cause there already exists one
        assertThatExceptionOfType(Exception.class).isThrownBy(() ->
            repositoryService.changeDeploymentTenantId(processDefinitionIdWithTenant, "")
        );

        // Deploying another version should just up the version
        String processDefinitionIdNoTenant2 = deployOneTaskTestProcess();
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionIdNoTenant2)
                .singleResult()
                .getVersion()
        ).isEqualTo(2);
    }

    public void testJobTenancyAfterTenantChange() {
        // Deploy process with a timer and an async step AND with a tenant
        String deploymentId = repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy()
            .getId();

        String newTenant = "newTenant";
        repositoryService.changeDeploymentTenantId(deploymentId, newTenant);

        // verify job (timer start)
        Job job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(newTenant);
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());

        // Verify Job tenancy (process intermediary timer)
        job = managementService.createTimerJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(newTenant);

        // Start process, and verify async job has correct tenant id
        managementService.moveTimerToExecutableJob(job.getId());
        managementService.executeJob(job.getId());
        job = managementService.createJobQuery().singleResult();
        assertThat(job.getTenantId()).isEqualTo(newTenant);

        // Finish process
        managementService.executeJob(job.getId());

        // clean up
        repositoryService.deleteDeployment(deploymentId, true);
    }

    public void testHistoryTenancy() {
        if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.AUDIT)) {
            // Generate 3 tasks with tenant
            String processDefinitionIdWithTenant = deployTestProcessWithTestTenant();
            int nrOfProcessInstancesWithTenant = 3;
            for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
                runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
            }

            // Generate 2 tasks without tenant
            String processDefinitionIdNoTenant = deployOneTaskTestProcess();
            int nrOfProcessInstancesNoTenant = 2;
            for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
                runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
            }

            // Complete all tasks
            for (Task task : taskService.createTaskQuery().list()) {
                taskService.complete(task.getId());
            }

            // Verify process instances
            assertThat(
                historyService
                    .createHistoricProcessInstanceQuery()
                    .processDefinitionId(processDefinitionIdWithTenant)
                    .list()
                    .get(0)
                    .getTenantId()
            ).isEqualTo(TEST_TENANT_ID);
            assertThat(
                historyService
                    .createHistoricProcessInstanceQuery()
                    .processDefinitionId(processDefinitionIdNoTenant)
                    .list()
                    .get(0)
                    .getTenantId()
            ).isEqualTo("");
            assertThat(historyService.createHistoricProcessInstanceQuery().list()).hasSize(
                nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant
            );
            assertThat(
                historyService.createHistoricProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list()
            ).hasSize(nrOfProcessInstancesWithTenant);
            assertThat(
                historyService.createHistoricProcessInstanceQuery().processInstanceTenantIdLike("%e%").list()
            ).hasSize(nrOfProcessInstancesWithTenant);
            assertThat(
                historyService.createHistoricProcessInstanceQuery().processInstanceWithoutTenantId().list()
            ).hasSize(nrOfProcessInstancesNoTenant);

            // verify tasks
            assertThat(
                historyService
                    .createHistoricTaskInstanceQuery()
                    .processDefinitionId(processDefinitionIdWithTenant)
                    .list()
                    .get(0)
                    .getTenantId()
            ).isEqualTo(TEST_TENANT_ID);
            assertThat(
                historyService
                    .createHistoricTaskInstanceQuery()
                    .processDefinitionId(processDefinitionIdNoTenant)
                    .list()
                    .get(0)
                    .getTenantId()
            ).isEqualTo("");
            assertThat(historyService.createHistoricTaskInstanceQuery().list()).hasSize(
                nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant
            );
            assertThat(historyService.createHistoricTaskInstanceQuery().taskTenantId(TEST_TENANT_ID).list()).hasSize(
                nrOfProcessInstancesWithTenant
            );
            assertThat(historyService.createHistoricTaskInstanceQuery().taskTenantIdLike("my%").list()).hasSize(
                nrOfProcessInstancesWithTenant
            );
            assertThat(historyService.createHistoricTaskInstanceQuery().taskWithoutTenantId().list()).hasSize(
                nrOfProcessInstancesNoTenant
            );

            // verify activities
            List<HistoricActivityInstance> activityInstances = historyService
                .createHistoricActivityInstanceQuery()
                .processDefinitionId(processDefinitionIdWithTenant)
                .list();
            for (HistoricActivityInstance historicActivityInstance : activityInstances) {
                assertThat(historicActivityInstance.getTenantId()).isEqualTo(TEST_TENANT_ID);
            }
            assertThat(
                historyService
                    .createHistoricActivityInstanceQuery()
                    .processDefinitionId(processDefinitionIdNoTenant)
                    .list()
                    .get(0)
                    .getTenantId()
            ).isEqualTo("");
            assertThat(historyService.createHistoricActivityInstanceQuery().list()).hasSize(
                3 * (nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant)
            );
            assertThat(
                historyService.createHistoricActivityInstanceQuery().activityTenantId(TEST_TENANT_ID).list()
            ).hasSize(3 * nrOfProcessInstancesWithTenant);
            assertThat(historyService.createHistoricActivityInstanceQuery().activityTenantIdLike("my%").list()).hasSize(
                3 * nrOfProcessInstancesWithTenant
            );
            assertThat(historyService.createHistoricActivityInstanceQuery().activityWithoutTenantId().list()).hasSize(
                3 * nrOfProcessInstancesNoTenant
            );
        }
    }

    public void testProcessDefinitionKeyClashBetweenTenants() {
        String tentanA = "tenantA";
        String tenantB = "tenantB";

        // Deploy the same process (same process definition key) for two different tenants.
        String procDefIdA = deployTestProcessWithTestTenant(tentanA);
        String procDefIdB = deployTestProcessWithTestTenant(tenantB);

        // verify query
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getKey()
        ).isEqualTo("oneTaskProcess");
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getVersion()
        ).isEqualTo(1);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getKey()
        ).isEqualTo("oneTaskProcess");
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getVersion()
        ).isEqualTo(1);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").list()
        ).hasSize(2);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tentanA)
                .list()
        ).hasSize(1);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tenantB)
                .list()
        ).hasSize(1);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionWithoutTenantId()
                .list()
        ).hasSize(0);

        // Deploy second version
        procDefIdA = deployTestProcessWithTestTenant(tentanA);

        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getKey()
        ).isEqualTo("oneTaskProcess");
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getVersion()
        ).isEqualTo(2);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tentanA)
                .latestVersion()
                .singleResult()
                .getVersion()
        ).isEqualTo(2);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getKey()
        ).isEqualTo("oneTaskProcess");
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getVersion()
        ).isEqualTo(1);
        assertThat(
            repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").list().size()
        ).isEqualTo(3);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tentanA)
                .list()
                .size()
        ).isEqualTo(2);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tenantB)
                .list()
                .size()
        ).isEqualTo(1);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionTenantId(tentanA)
                .latestVersion()
                .list()
                .size()
        ).isEqualTo(1);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey("oneTaskProcess")
                .processDefinitionWithoutTenantId()
                .list()
                .size()
        ).isEqualTo(0);

        // Now, start process instances by process definition key (no tenant)
        // shouldn't happen, there is no process definition with that
        // key that has no tenant, it has to give an exception as such!
        assertThatExceptionOfType(Exception.class).isThrownBy(() ->
            runtimeService.startProcessInstanceByKey("oneTaskProcess")
        );

        ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(
            "oneTaskProcess",
            tentanA
        );
        assertThat(processInstance.getProcessDefinitionId()).isEqualTo(procDefIdA);

        processInstance = runtimeService.startProcessInstanceByKeyAndTenantId("oneTaskProcess", tenantB);
        assertThat(processInstance.getProcessDefinitionId()).isEqualTo(procDefIdB);
    }

    public void testSuspendProcessDefinitionTenancy() {
        // Deploy one process definition for tenant A, and two process
        // definitions versions for tenant B
        String tentanA = "tenantA";
        String tenantB = "tenantB";

        String procDefIdA = deployTestProcessWithTestTenant(tentanA);
        String procDefIdB = deployTestProcessWithTestTenant(tenantB);
        String procDefIdB2 = deployTestProcessWithTestTenant(tenantB);

        // Suspend process definition B
        repositoryService.suspendProcessDefinitionByKey("oneTaskProcess", tenantB);

        // Shouldn't be able to start proc defs for tentant B
        try {
            runtimeService.startProcessInstanceById(procDefIdB);
        } catch (ActivitiException e) {}

        try {
            runtimeService.startProcessInstanceById(procDefIdB2);
        } catch (ActivitiException e) {}

        ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefIdA);
        assertThat(processInstance).isNotNull();

        // Activate process again
        repositoryService.activateProcessDefinitionByKey("oneTaskProcess", tenantB);

        processInstance = runtimeService.startProcessInstanceById(procDefIdB);
        assertThat(processInstance).isNotNull();

        processInstance = runtimeService.startProcessInstanceById(procDefIdB2);
        assertThat(processInstance).isNotNull();

        processInstance = runtimeService.startProcessInstanceById(procDefIdA);
        assertThat(processInstance).isNotNull();

        // Suspending with NO tenant id should give an error, cause they both
        // have tenants
        try {
            repositoryService.suspendProcessDefinitionByKey("oneTaskProcess");
        } catch (ActivitiException e) {}
    }

    public void testSignalFromProcessTenancy() {
        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Start 3 proc instances for the one with a tenant and 2 for the one
        // without tenant
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");

        // verify
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);

        // Now, start 1 process instance that fires a signal event (not in tenant context), it should only continue those without tenant
        runtimeService.startProcessInstanceByKey("testMtSignalFiring");
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(0);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            2
        );

        // Start a process instance that is running in tenant context
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalFiring", TEST_TENANT_ID);
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(3);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            2
        );

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testSignalThroughApiTenancy() {
        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Start 4 proc instances for the one with a tenant and 5 for the one without tenant
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");

        // Verify
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);

        // Signal through API (with tenant)
        runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            0
        );

        // Signal through API (without tenant)
        runtimeService.signalEventReceived("The Signal");
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            5
        );

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testSignalThroughApiTenancyReversed() {
        // cause reversing the
        // order of calling DID leave to an error!

        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Start 4 proc instances for the one with a tenant and 5 for the one without tenant
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");

        // Verify
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);

        // Signal through API (without tenant)
        runtimeService.signalEventReceived("The Signal");
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(0);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            5
        );

        // Signal through API (with tenant)
        runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            5
        );

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testSignalAsyncThroughApiTenancy() {
        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Start 4 proc instances for the one with a tenant and 5 for the one
        // without tenant
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");
        runtimeService.startProcessInstanceByKey("testMtSignalCatch");

        // Verify
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);

        // Signal through API (with tenant)
        runtimeService.signalEventReceivedAsyncWithTenantId("The Signal", TEST_TENANT_ID);
        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(0);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            0
        );

        for (Job job : managementService.createJobQuery().list()) {
            managementService.executeJob(job.getId());
        }

        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            0
        );

        // Signal through API (without tenant)
        runtimeService.signalEventReceivedAsync("The Signal");

        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            0
        );

        for (Job job : managementService.createJobQuery().list()) {
            managementService.executeJob(job.getId());
        }

        assertThat(
            taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);
        assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
            5
        );

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testStartProcessInstanceBySignalTenancy() {
        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource(
                "org/activiti/engine/test/api/tenant/TenancyTest.testStartProcessInstanceBySignalTenancy.bpmn20.xml"
            )
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource(
                "org/activiti/engine/test/api/tenant/TenancyTest.testStartProcessInstanceBySignalTenancy.bpmn20.xml"
            )
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Signaling without tenant
        runtimeService.signalEventReceived("The Signal");
        assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(3);
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(3);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(0);

        // Signalling with tenant
        runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
        assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(6);
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(3);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(3);

        // Start a process instance with a boundary catch (with and without tenant)
        runtimeService.startProcessInstanceByKey("processWithSignalCatch");
        runtimeService.startProcessInstanceByKeyAndTenantId("processWithSignalCatch", TEST_TENANT_ID);

        runtimeService.signalEventReceived("The Signal");
        assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(11);
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(7);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(4);

        runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
        assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(14);
        assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(7);
        assertThat(
            runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
        ).isEqualTo(7);

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testStartProcessInstanceByMessageTenancy() {
        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Verify query
        assertThat(
            repositoryService.createProcessDefinitionQuery().messageEventSubscriptionName("My message").count()
        ).isEqualTo(2);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .messageEventSubscriptionName("My message")
                .processDefinitionWithoutTenantId()
                .count()
        ).isEqualTo(1);
        assertThat(
            repositoryService
                .createProcessDefinitionQuery()
                .messageEventSubscriptionName("My message")
                .processDefinitionTenantId(TEST_TENANT_ID)
                .count()
        ).isEqualTo(1);

        // Start a process instance by message without tenant
        runtimeService.startProcessInstanceByMessage("My message");
        runtimeService.startProcessInstanceByMessage("My message");

        assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(2);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(0);

        // Start a process instance by message with tenant
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(5);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    public void testStartProcessInstanceByMessageTenancyReversed() {
        // same as
        // above, but now reversed

        // Deploy process both with and without tenant
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
            .deploy();
        repositoryService
            .createDeployment()
            .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
            .tenantId(TEST_TENANT_ID)
            .deploy();

        // Start a process instance by message with tenant
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
        assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(3);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(0);
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);

        // Start a process instance by message without tenant
        runtimeService.startProcessInstanceByMessage("My message");
        runtimeService.startProcessInstanceByMessage("My message");

        assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(5);
        assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
        assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);

        // Cleanup
        for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
            repositoryService.deleteDeployment(deployment.getId(), true);
        }
    }

    // Bug from http://forums.activiti.org/content/callactiviti-tenant-id
    public void testCallActivityWithTenant() {
        if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.ACTIVITY)) {
            String tenantId = "apache";

            // deploying both processes. Process 1 will call Process 2
            repositoryService
                .createDeployment()
                .addClasspathResource(
                    "org/activiti/engine/test/api/tenant/TenancyTest.testCallActivityWithTenant-process01.bpmn20.xml"
                )
                .tenantId(tenantId)
                .deploy();
            repositoryService
                .createDeployment()
                .addClasspathResource(
                    "org/activiti/engine/test/api/tenant/TenancyTest.testCallActivityWithTenant-process02.bpmn20.xml"
                )
                .tenantId(tenantId)
                .deploy();

            // Starting Process 1. Process 1 will be executed successfully but
            // when the call to process 2 is made internally it will throw the
            // exception
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(
                "process1",
                null,
                singletonMap("sendFor", "test"),
                tenantId
            );
            assertThat(processInstance).isNotNull();

            assertThat(
                historyService
                    .createHistoricProcessInstanceQuery()
                    .processDefinitionKey("process2")
                    .processInstanceTenantId(tenantId)
                    .count()
            ).isEqualTo(1);
            assertThat(
                historyService.createHistoricProcessInstanceQuery().processDefinitionKey("process2").count()
            ).isEqualTo(1);

            // following line if executed will give activiti object not found
            // exception as the process1 is linked to a tenant id.
            assertThatExceptionOfType(Exception.class).isThrownBy(() ->
                runtimeService.startProcessInstanceByKey("process1")
            );

            // Cleanup
            for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
                repositoryService.deleteDeployment(deployment.getId(), true);
            }
        }
    }

    /*
     * See https://activiti.atlassian.net/browse/ACT-4034
     */
    public void testGetLatestProcessDefinitionVersionForSameProcessDefinitionKey() {
        String tenant1 = "tenant1";
        String tenant2 = "tenant2";

        // Tenant 1 ==> version 4
        for (int i = 0; i < 4; i++) {
            deployTestProcessWithTestTenant(tenant1);
        }

        // Tenant 2 ==> version 2
        for (int i = 0; i < 2; i++) {
            deployTestProcessWithTestTenant(tenant2);
        }

        // No tenant ==> version 3
        for (int i = 0; i < 3; i++) {
            deployTestProcessWithTwoTasksNoTenant();
        }

        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionTenantId(tenant1)
            .latestVersion()
            .singleResult();
        assertThat(processDefinition.getVersion()).isEqualTo(4);

        processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionTenantId(tenant2)
            .latestVersion()
            .singleResult();
        assertThat(processDefinition.getVersion()).isEqualTo(2);

        processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionWithoutTenantId()
            .latestVersion()
            .singleResult();
        assertThat(processDefinition.getVersion()).isEqualTo(3);

        List<ProcessDefinition> processDefinitions = repositoryService
            .createProcessDefinitionQuery()
            .latestVersion()
            .list();
        assertThat(processDefinitions.size()).isEqualTo(3);

        // Verify they have different tenant ids
        int tenant1Count = 0,
            tenant2Count = 0,
            noTenantCount = 0;
        for (ProcessDefinition p : processDefinitions) {
            if (p.getTenantId() == null || p.getTenantId().equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
                noTenantCount++;
            } else if (p.getTenantId().equals(tenant1)) {
                tenant1Count++;
            } else if (p.getTenantId().equals(tenant2)) {
                tenant2Count++;
            }
        }
        assertThat(tenant1Count).isEqualTo(1);
        assertThat(tenant2Count).isEqualTo(1);
        assertThat(noTenantCount).isEqualTo(1);
    }
}
